#include <iostream>
#include <vector>
#include <cstring>

using namespace std;


template<typename dataType>
class MyVector{
private:
    dataType* data_ = nullptr;

    int capacity_ = 0;
    int size_ = 0;

public:
    MyVector(){
        // printf("default construct\n");
    }

    MyVector(int size, const dataType& value){
        data_ = new dataType[size];
        capacity_ = size;
        size_ = size;

        // memset(data_, value, size_*sizeof(dataType));
        for (int i=0;i<size_;++i){
            data_[i] = value;
        }
    }

    MyVector(const MyVector& other)
    {
        *this = other;
    }

    virtual ~MyVector(){
        delete [] data_;
    }

    void push(const dataType& value){
        if (size_ == capacity_){
            enlargeCapacity();
        }

        data_[size_] = value;
        size_ += 1;
    }

    void clear(){
        delete [] data_;
        data_ = nullptr;

        size_ = 0;
        capacity_ = 0;
    }

    void resize(int newSize, const dataType& capa){
        delete [] data_;
        data_ = new dataType[newSize];
        size_ = newSize;
        capacity_ = newSize;

        for (int i=0;i<newSize;++i){
            data_[i] = capa;
        }
    }

public:
    MyVector& operator= (const MyVector& other){
        this->size_ = other.size_;
        this->capacity_ = other.capacity();     
        this->data_ = new dataType[capacity_];
        memcpy(this->data_, other.data_, size_);
        printf("operator= capacity this %d other %d\n", this->capacity_, other.capacity_);   
    }
    MyVector& operator= (MyVector&& other){
        this->size_ = other.size();
        this->capacity_ = other.capacity();
        this->data_ = other.data();

        // other.clear();
        other.size_ = 0;
        other.capacity_ = 0;
        other.data_ = nullptr;
    }

private:
    void enlargeCapacity(){
        if (capacity_ == 0){
            data_ = new dataType[1];
            capacity_ = 1;
            size_ = 1;
        }

        int newCapacity_ = capacity_ * 2;
        dataType* newData = new dataType[newCapacity_];

        memcpy(newData, data_, capacity_*sizeof(dataType));

        delete [] data_;

        data_ = newData;
        capacity_ = newCapacity_;
    }

public:
    int size() const{
        return size_;
    }
    int capacity() const{
        return capacity_;
    }
    dataType* data() const{
        return data_;
    }
};


template<typename datatype>
class MySharedPointer{
private:
    struct MyStorage
    {
        datatype* dataPtr_ = nullptr;
        int counter_ = 0;
    };
    
private:
    MyStorage* storagePtr_ = nullptr;

public:
    datatype* data() const {
        return storagePtr_->dataPtr_;
    }

    datatype* operator->() const {
        return storagePtr_->dataPtr_;
    }

    datatype& operator* () const {
        return *(storagePtr_->dataPtr_);
    }

public:
    MySharedPointer() = default;
    MySharedPointer(void * ptr){
        if (storagePtr_ == nullptr){
            storagePtr_ = new MyStorage;
            storagePtr_->counter_ = 1;
            storagePtr_->dataPtr_ = static_cast<datatype*>(ptr);
        }else{
            if (storagePtr_->dataPtr_ == ptr){
                storagePtr_->counter_ += 1;
                return;
            }

            if (storagePtr_->counter_ == 1){delete storagePtr_->dataPtr_;}

            storagePtr_->dataPtr_ = static_cast<datatype*>(ptr);
            storagePtr_->counter_ = 1;
        }
    }

    static MySharedPointer<datatype> MyMakeShared(const datatype& value){
        return MySharedPointer<datatype>(new datatype(value));
    }

    ~MySharedPointer(){
        if (! storagePtr_){
            return;
        }else{
            storagePtr_->counter_ -= 1;
            if (storagePtr_->counter_ == 0){
                delete storagePtr_->dataPtr_;
                delete storagePtr_;
            }
        }
    }

public:
    MySharedPointer(const MySharedPointer<datatype>& other){
        if (&other == this){
            return;
        }
printf("construct inside copy %p\n", other.storagePtr_->dataPtr_);
        storagePtr_ = other.storagePtr_;
        if (storagePtr_){
            storagePtr_->counter_ += 1;
        }
    }
    MySharedPointer(MySharedPointer<datatype>&& other){
printf("construct inside move %p\n", other.storagePtr_->dataPtr_);
        storagePtr_ = other.storagePtr_;
        other.storagePtr_ = nullptr;
    }
    MySharedPointer<datatype>& operator= (const MySharedPointer<datatype>& other){
        if (&other == this){
            return *this;
        }
printf("inside copy %p\n", other.storagePtr_->dataPtr_);
        storagePtr_ = other.storagePtr_;
        if (storagePtr_){
            storagePtr_->counter_ += 1;
        }
        return *this;
    }
    MySharedPointer<datatype>& operator= (MySharedPointer<datatype>&& other){
printf("inside move %p\n", other.storagePtr_->dataPtr_);
        storagePtr_ = other.storagePtr_;
        other.storagePtr_ = nullptr;
        return *this;
    }
};

struct logData
{
    int data = 0;

    // void* operator new (std::size_t size) throw (std::bad_alloc);
    // void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();

    void* operator new(size_t size)
    {
        auto ptr = malloc(size);
        printf("new logData Construct %p\n", ptr);
        return ptr;
    }
 
    // void* operator new(size_t size, const std::nothrow_t& nothrow_value)
    // {
    //     std::cout<<"call A::operator new nothrow"<<std::endl;
    //     return malloc(size);
    // }

    logData(){
        printf("logData Construct %p\n", this);
    }
    ~logData(){
        printf("logData Deconstruct %p\n", this);
    }
};


int main() {
    // {
    //     MySharedPointer<logData> ptr1(new logData);
    // }

    // {
    //     MySharedPointer<logData> ptr2;
    //     {
    //         MySharedPointer<logData> ptr1(new logData);
    //         ptr2 = ptr1;
    //         printf("copy\n");
    //     }
    //     printf("still one counter\n");
    // }

    // {
    //     {
    //         MySharedPointer<logData> ptr2;
    //         MySharedPointer<logData> ptr1(new logData);
    //         ptr2 = std::move(ptr1);
    //         printf("move\n");
    //     }
    //     printf("no counter left\n");
    // }

    {
        logData testData;
        auto ptr = MySharedPointer<logData>::MyMakeShared(testData);
    }

    // ------------------------------------

    // MyVector<int> vecA(5, 1);

    // MyVector<int> vecTest;
    // for (int i=0;i<100;++i){
    //     vecTest.push(i);
    //     printf("i %d vec size %d capacity %d \n", i, vecTest.size(), vecTest.capacity());
    // }

    return 1;
}